using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;
using Microsoft.Surface;
using Microsoft.Surface.Presentation;
using Microsoft.Surface.Presentation.Controls;
using System.Threading;
using System.ComponentModel;
using System.Windows.Forms;


namespace LetsGoToSchool
{
    /// <summary>
    /// Interaction logic for MusicGame.xaml
    /// </summary>
    public partial class MusicGame : SurfaceWindow
    {
        private MediaPlayer _songPlayer;
        private Dictionary<String, MediaPlayer> _players;
        private List<Rectangle> _whiteKeys, _blackKeys;
        private Dictionary<String, Rectangle> _keys;
        private Dictionary<Rectangle, String> _keyToNote;
        private Dictionary<DispatcherTimer, Rectangle> _timerRects;
        private static List<Note> _notes;
        private static Rectangle _current = null;
        private static Brush _currentColor = null;
        private bool _isPaused, _isStopped;
        private BackgroundWorker _worker;
        private Stack<MusicStatNode> _musicStats = new Stack<MusicStatNode>();
        private MusicStatNode _currStatNode;
        private string _song;
        private double _numCorrect, _totalNumNotes;

        /// <summary>
        /// Default constructor.
        /// </summary>
        /// 
        public MusicGame()
        {
            InitializeComponent();
            _selection.setMain(this);
            // Add handlers for Application activation events
            AddActivationHandlers();
            Contacts.AddContactTapGestureHandler(Stop, Stop_TapHandler);
            Contacts.AddContactTapGestureHandler(Play, PlayPause_TapHandler);
            Contacts.AddContactTapGestureHandler(Volume, Mute_TapHandler);
            _isPaused = false;
            _songPlayer = new MediaPlayer();
            _songPlayer.MediaOpened += new EventHandler(_songPlayer_MediaOpened);
            _songPlayer.MediaEnded += new EventHandler(songPlayer_MediaEnded);
            _songPlayer.Volume = 0.5;
            _timerRects = new Dictionary<DispatcherTimer,Rectangle>();
            _worker = new BackgroundWorker();
            _worker.DoWork += Worker_DoWork;
            _numCorrect = 0;
            _totalNumNotes = 0;
            _song = "Free Play";
            _isStopped = false;

            _whiteKeys = new List<Rectangle>();
            _blackKeys = new List<Rectangle>();
            
            //draws the white keys
            for (int i = 0; i < 22; i++)
            {
                Rectangle whiteKey = new Rectangle();
                whiteKey.Fill = new SolidColorBrush(Colors.White);
                whiteKey.Visibility = Visibility.Visible;
                whiteKey.StrokeThickness = 2;
                whiteKey.Stroke = new SolidColorBrush(Colors.Black);
                whiteKey.Height = 250;
                whiteKey.Width = 39.6;
                MusicCanvas.Children.Add(whiteKey);
                Canvas.SetTop(whiteKey, 450);
                Canvas.SetLeft(whiteKey, 54 + 39.6 * i);
                _whiteKeys.Add(whiteKey);

                whiteKey.MouseDown += Key_play;
                Contacts.AddContactTapGestureHandler(whiteKey, Key_play);
            }
            //draws the black keys
           for (int i = 0; i < 21; i++)
            {
                if (i != 2 && i != 6 && i != 9 && i != 13 && i != 16 && i != 20)
                {
                    Rectangle blackKey = new Rectangle();
                    blackKey.Fill = new SolidColorBrush(Colors.Black);
                    blackKey.Visibility = Visibility.Visible;
                    blackKey.StrokeThickness = 2;
                    blackKey.Stroke = new SolidColorBrush(Colors.Black);
                    blackKey.Height = 125;
                    blackKey.Width = 26.4;
                    MusicCanvas.Children.Add(blackKey);
                    Canvas.SetTop(blackKey, 450);
                    Canvas.SetLeft(blackKey, 80.4 + 39.6 * i);
                    _blackKeys.Add(blackKey);

                    blackKey.MouseDown += Key_play;
                    Contacts.AddContactTapGestureHandler(blackKey, Key_play);
                }
            }

           _keys = new Dictionary<string, Rectangle>();
           _keys.Add("C3", _whiteKeys[0]);
           _keys.Add("D3", _whiteKeys[1]);
           _keys.Add("E3", _whiteKeys[2]);
           _keys.Add("F3", _whiteKeys[3]);
           _keys.Add("G3", _whiteKeys[4]);
           _keys.Add("A3", _whiteKeys[5]);
           _keys.Add("B3", _whiteKeys[6]);
           _keys.Add("C4", _whiteKeys[7]);
           _keys.Add("D4", _whiteKeys[8]);
           _keys.Add("E4", _whiteKeys[9]);
           _keys.Add("F4", _whiteKeys[10]);
           _keys.Add("G4", _whiteKeys[11]);
           _keys.Add("A4", _whiteKeys[12]);
           _keys.Add("B4", _whiteKeys[13]);
           _keys.Add("C5", _whiteKeys[14]);
           _keys.Add("D5", _whiteKeys[15]);
           _keys.Add("E5", _whiteKeys[16]);
           _keys.Add("F5", _whiteKeys[17]);
           _keys.Add("G5", _whiteKeys[18]);
           _keys.Add("A5", _whiteKeys[19]);
           _keys.Add("B5", _whiteKeys[20]);
           _keys.Add("C6", _whiteKeys[21]);
           _keys.Add("Cs3", _blackKeys[0]);
           _keys.Add("Ds3", _blackKeys[1]);
           _keys.Add("Fs3", _blackKeys[2]);
           _keys.Add("Gs3", _blackKeys[3]);
           _keys.Add("Bb3", _blackKeys[4]);
           _keys.Add("Cs4", _blackKeys[5]);
           _keys.Add("Ds4", _blackKeys[6]);
           _keys.Add("Fs4", _blackKeys[7]);
           _keys.Add("Gs4", _blackKeys[8]);
           _keys.Add("Bb4", _blackKeys[9]);
           _keys.Add("Cs5", _blackKeys[10]);
           _keys.Add("Ds5", _blackKeys[11]);
           _keys.Add("Fs5", _blackKeys[12]);
           _keys.Add("Gs5", _blackKeys[13]);
           _keys.Add("Bb5", _blackKeys[14]);

           _keyToNote = new Dictionary<Rectangle, String>();
           foreach (String note in _keys.Keys)
           {
               _keyToNote.Add(_keys[note], note);
           }

           _players = new Dictionary<String, MediaPlayer>();
           foreach (string note in _keys.Keys)
           {
               _players.Add(note, new MediaPlayer());
           }

           foreach (string note in _players.Keys)
           {
               MediaPlayer mp = _players[note];
               mp.MediaEnded += new EventHandler(player_MediaEnded);
               mp.Volume = 0.5;
               mp.Open(new Uri(@"C:/Users/admin/Desktop/CS32/lets-go-to-school/LetsGoToSchool/Resources/" + note + ".wav"));
           }

            VolumnBar.Value = 5;
            foreach (Rectangle key in _keys.Values)
            {
                DispatcherTimer timer = new DispatcherTimer();
                timer.Interval = TimeSpan.FromSeconds(.5);
                timer.Tick += new EventHandler(correctness_Listener);
                timer.Tag = key;
                key.Tag = timer;
                
            }
        }

        /// <summary>
        /// Timer listener for timer that helps with checking if the user has hit the correct key at the correct time.
        /// </summary>
        /// 
        void correctness_Listener(object sender, EventArgs e)
        {
            (sender as DispatcherTimer).Stop();
        }

        /// <summary>
        /// This makes the loading message show until the song has fully loaded.
        /// </summary>
        /// 
        void _songPlayer_MediaOpened(object sender, EventArgs e)
        {
            NoContactCanvas.Visibility = Visibility.Hidden;
            _loading.Visibility = Visibility.Hidden;
            _songPlayer.Play();
            _worker.RunWorkerAsync();
        }

        /// <summary>
        /// This is the method that is run when the thread is started.  It goes through each note
        /// that was in the xml file and lights up the key as the file plays.  It makes sure that
        /// it is not paused or stopped and deals accordingly if it is.
        /// </summary>
        /// 

        public void Worker_DoWork(object sender, DoWorkEventArgs e)
        {
            foreach (Note n in _notes)
            {
                if (_isStopped)
                {
                    break;
                }
                while (!_isStopped)
                {
                    if (!_isPaused)
                    {
                        break;
                    }
                }

                _current = _keys[n.getName()];
                //light up the key
                _current.Dispatcher.BeginInvoke((MethodInvoker)delegate()
                {
                    _currentColor = _current.Fill;
                    _current.Fill = new SolidColorBrush(Colors.Red);
                    (_current.Tag as DispatcherTimer).Start();
                });
                Thread.Sleep(TimeSpan.FromMilliseconds(n.getLength() * .5));
                _current.Dispatcher.BeginInvoke((MethodInvoker)delegate()
                {
                    _current.Fill = _currentColor;
                });
                Thread.Sleep(TimeSpan.FromMilliseconds(n.getLength() * .5));
                _totalNumNotes++;
            }

            _current = null;
            //If the song has finished, try to write the results to the currStatNode
            if (_currStatNode != null)
            {
                if (_totalNumNotes != 0)
                {
                    _currStatNode.setPercentCorrect(_numCorrect / _totalNumNotes);
                }
                else
                {
                    _currStatNode.setPercentCorrect(0);
                }
                _currStatNode.EndTime = DateTime.Now;
                _musicStats.Push(_currStatNode);
            }
            //reset everything
            _currStatNode = null;
            _totalNumNotes = 0;
            _numCorrect = 0;
            _correctLabel.Dispatcher.BeginInvoke((MethodInvoker)delegate()
            {
                _correctLabel.Text = "Number Correct: 0";
            });
        }

        /// <summary>
        /// This is the event handler for the mute button, which sets the volume to 0 when tapped.
        /// </summary>
        /// 
        public void Mute_TapHandler(object sender, ContactEventArgs e)
        {
            if (_songPlayer.Volume == 0)
            {
                if (VolumnBar.Tag != null)
                    VolumnBar.Value = (double)VolumnBar.Tag;
                else
                    VolumnBar.Value = 5;
            }
            else
            {
                VolumnBar.Tag = VolumnBar.Value;//remember the volumn before mute
                VolumnBar.Value = 0;
            }
        }

        /// <summary>
        /// This is the event handler for the stop button.  This stops the music and the keys from lighting up
        /// and resets everything necessary.
        /// </summary>
        /// 
        public void Stop_TapHandler(object sender, ContactEventArgs e)
        {
            //stop the song and reset it
            _songPlayer.Stop();
            _songPlayer.Position = TimeSpan.FromSeconds(0);
            Utils.changeSource(Play, "pack://application:,,,/Resources/play-icon.png");
            //reset everything
            _isPaused = true;
            _isStopped = true;
            _worker.Dispose();
            //write to _currStatNode if it is not null and then add it to the list of MusicStatNodes
            if (_currStatNode != null)
            {
                if (_totalNumNotes != 0)
                {
                    _currStatNode.setPercentCorrect(_numCorrect / _totalNumNotes);
                }
                else
                {
                    _currStatNode.setPercentCorrect(0);
                }
                _totalNumNotes = 0;
                _numCorrect = 0;
                _currStatNode.EndTime = DateTime.Now;
                _musicStats.Push(_currStatNode);
            }
            _currStatNode = null;
            _correctLabel.Text = "Number Correct: " + _numCorrect;
        }

        /// <summary>
        /// THis is the event handler for the play/pause button.  It plays or pauses the music and changes the icon
        /// of the button depending on what its function is at that time.
        /// </summary>
        /// 
        public void PlayPause_TapHandler(object sender, ContactEventArgs e)
        {
            //if it is stopped then it makes a new stat node and resets everything.
            if (_isStopped)
            {
                _currStatNode = new MusicStatNode(DateTime.Now, _song, 1);
                _isStopped = false;
                _worker = new BackgroundWorker();
                _worker.DoWork += Worker_DoWork;
                _worker.RunWorkerAsync();
                _songPlayer.Play();
                _isPaused = false;
                Utils.changeSource(Play, "pack://application:,,,/Resources/pause-icon.png");
            }
            //else if it is paused, play the song from the current position
            else if (_isPaused)
            {
                _songPlayer.Play();
                _isPaused = false;
                Utils.changeSource(Play, "pack://application:,,,/Resources/pause-icon.png");
            }
            //otherwise pause the song
            else
            {
                _songPlayer.Pause();
                _isPaused = true;
                Utils.changeSource(Play, "pack://application:,,,/Resources/play-icon.png");
            }
        }

        /// <summary>
        /// Accessor to return the file name of the icon for the game
        /// </summary>
        /// 
        public static String getIconFileName()
        {
            return "Icon-Music.jpg";
        }

        /// <summary>
        /// Accessor to return the name of the song selected.
        /// </summary>
        /// 
        public string getSong()
        {
            return _song;
        }

        /// <summary>
        /// Method to start the game when it is in free play mode.
        /// This resets everything and makes the game visible.
        /// </summary>
        /// 
        public void startGame()
        {
            _isStopped = false;
            _isPaused = false;
            MainCanvas.Visibility = Visibility.Visible;
            _song = "Free Play";
            _currStatNode = new MusicStatNode(DateTime.Now, "Free Play", 1);

            ButtonCanvas.Visibility = Visibility.Hidden;
            NoContactCanvas.Visibility = Visibility.Hidden;
            _loading.Visibility = Visibility.Hidden;
            _correctLabel.Text = "Number Correct: 0";
        }

        /// <summary>
        /// Method to start the game when a song has been selected.
        /// </summary>
        /// 
        public void startGame(string song)
        {
            
            Utils.Soon(delegate {
                //reset everything
                _isStopped = false;
                _isPaused = false;
                Utils.changeSource(Play, "pack://application:,,,/Resources/pause-icon.png");
                _notes = XMLParser.readXML("C:/Users/admin/Desktop/CS32/lets-go-to-school/LetsGoToSchool/Resources/" + song + ".xml");
                _songPlayer.Open(new Uri(@"C:/Users/admin/Desktop/CS32/lets-go-to-school/LetsGoToSchool/Resources/" + song + ".wav"));
                if (!song.Equals(_song))
                {
                    NoContactCanvas.Visibility = Visibility.Visible;
                    _loading.Visibility = Visibility.Visible;
                }
                //if it is the same song that was previously played, play the song and start lighting up the keys
                else
                {
                    _songPlayer.Play();
                    _worker.RunWorkerAsync();
                }
                _song = song;
                //create a new MusicStatNode to be the current one
                _currStatNode = new MusicStatNode(DateTime.Now, song, 1);
                MainCanvas.Visibility = Visibility.Visible;
                ButtonCanvas.Visibility = Visibility.Visible;
                _correctLabel.Text = "Number Correct: 0";
            });
        }

        /// <summary>
        /// Event handler for when the note has finished playing.  
        /// This resets the position to be 0 and stops the player.
        /// </summary>
        /// 
        private void player_MediaEnded(Object sender, EventArgs e)
        {
            (sender as MediaPlayer).Position = TimeSpan.FromSeconds(0);
            (sender as MediaPlayer).Stop();
        }

        /// <summary>
        /// Event handler for when the song has finished playing.  
        /// This resets the position to be 0 and stops the player.
        /// It also disposes of the worker thread.
        /// </summary>
        /// 
        private void songPlayer_MediaEnded(Object sender, EventArgs e)
        {
            (sender as MediaPlayer).Position = TimeSpan.FromSeconds(0);
            (sender as MediaPlayer).Stop();
            Utils.changeSource(Play, "pack://application:,,,/Resources/play-icon.png");
            _worker.Dispose();
            _isStopped = true;
            _isPaused = false;
        }

        /// <summary>
        /// Occurs when the window is about to close. 
        /// </summary>
        /// <param name="e"></param>
        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);

            foreach (MusicStatNode node in _musicStats)
            {
                Database.writeMusicSessionData(node);
            }

            // Remove handlers for Application activation events
            RemoveActivationHandlers();
        }

        /// <summary>
        /// Adds handlers for Application activation events.
        /// </summary>
        private void AddActivationHandlers()
        {
            // Subscribe to surface application activation events
            ApplicationLauncher.ApplicationActivated += OnApplicationActivated;
            ApplicationLauncher.ApplicationPreviewed += OnApplicationPreviewed;
            ApplicationLauncher.ApplicationDeactivated += OnApplicationDeactivated;
        }

        /// <summary>
        /// Removes handlers for Application activation events.
        /// </summary>
        private void RemoveActivationHandlers()
        {
            // Unsubscribe from surface application activation events
            ApplicationLauncher.ApplicationActivated -= OnApplicationActivated;
            ApplicationLauncher.ApplicationPreviewed -= OnApplicationPreviewed;
            ApplicationLauncher.ApplicationDeactivated -= OnApplicationDeactivated;
        }

        /// <summary>
        /// This is called when application has been activated.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnApplicationActivated(object sender, EventArgs e)
        {
            //TODO: enable audio, animations here
        }

        /// <summary>
        /// This is called when application is in preview mode.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnApplicationPreviewed(object sender, EventArgs e)
        {
            //TODO: Disable audio here if it is enabled

            //TODO: optionally enable animations here
        }

        /// <summary>
        ///  This is called when application has been deactivated.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnApplicationDeactivated(object sender, EventArgs e)
        {
            //TODO: disable audio, animations here
        }

        /// <summary>
        /// Event handler for when the a user plays a note.
        /// Makes the corresponding note to the key play.
        /// </summary>
        /// 
        public void Key_play(object sender, EventArgs e)
        {
            MediaPlayer player = _players[_keyToNote[(Rectangle)sender]];
            player.Stop();
            player.Position = TimeSpan.FromSeconds(0);
            player.Play();
            if (!_song.Equals("Free Play"))
            {
                if (((sender as Rectangle).Tag as DispatcherTimer).IsEnabled)
                {
                    _numCorrect++;
                }
                _correctLabel.Text = "Number Correct: " + _numCorrect;
            }
        }

        /// <summary>
        /// Event handler for when the user quits the music game.
        /// This checks if it needs to set the values for the current stat node.
        /// It also writes all of the nodes to the database.
        /// It resets all values.
        /// </summary>
        /// 
        private void Quit_Click(object sender, RoutedEventArgs e)
        {
            _selection.Visibility = Visibility.Hidden;
            _songPlayer.Stop();
            //if you need to change the values of the currstatnode, do so
            if (_currStatNode != null)
            {
                if (_totalNumNotes != 0)
                {
                    _currStatNode.setPercentCorrect(_numCorrect / _totalNumNotes);
                }
                else
                {
                    _currStatNode.setPercentCorrect(0);
                }
                _currStatNode.EndTime = DateTime.Now;
                _musicStats.Push(_currStatNode);
            }
            //reset all values
            _currStatNode = null;
            _isPaused = false;
            _isStopped = false;
            _totalNumNotes = 0;
            _numCorrect = 0;
            _correctLabel.Text = "Number Correct: " + _numCorrect;
            //all statnodes are written to the database in the close method
            this.Close();
        }

        /// <summary>
        /// Event handler for when the user starts a new song/changes to free play mode
        /// </summary>
        /// 
        private void StartNew_Click(object sender, RoutedEventArgs e)
        {
            //first calls stop to reset everything
            this.Stop_TapHandler(this, null);
            //sets visibility of game
            _selection.Visibility = Visibility.Visible;
            MainCanvas.Visibility = Visibility.Hidden;
            //resets correctness score
            _totalNumNotes = 0;
            _numCorrect = 0;
        }

        /// <summary>
        /// Event handler for when the user pushes Help.  This plays the help video (currently kung fu panda trailer).
        /// </summary>
        /// 
        private void Help_Click(object sender, RoutedEventArgs e)
        {
            NoContactCanvas.Visibility = Visibility.Visible;
            _helpBox.Visibility = Visibility.Visible;
            //I didn't check in the video, just comment the line out
            _helpBox.playMovie("C:/lgts-Images/kung-fu-panda-trailer.mpg");
        }

        /// <summary>
        /// Event handler for changing the volume.
        /// </summary>
        /// 
        private void VolumnBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            double v = VolumnBar.Value / 10;
            _songPlayer.Volume = v;

            foreach (MediaPlayer mp in _players.Values)
            {
                mp.Volume = v;
            }
            if (VolumnBar.Value == 0)
                Utils.changeSource(Volume, "pack://application:,,,/Resources/Icon_Mute.png");
            else
                Utils.changeSource(Volume, "pack://application:,,,/Resources/Icon_Unmute.png");

        }
    }

    /// <summary>
    /// This is the MusicStatNode class which is a subclass of GAmeStatNode.
    /// This is how all of the results from each music session are stored.  
    /// After the user quits the music game, they are all written to the database.
    /// </summary>
    /// 
        public class MusicStatNode : GameStatNode
        {
            private double _percentCorrect;
            private string _song;

            /// <summary>
            /// Default Constructor.
            /// @param start Time (always given now--the time when the node is created)
            /// @param song the name of the song
            /// @param difficulty --the difficulty level of the game
            /// </summary>
            /// 
            public MusicStatNode(DateTime startTime, string song, int difficulty)
            {
                _startTime = startTime;
                _percentCorrect = 0;
                _song = song;
            }

            private DateTime _startTime;
            private DateTime _endTime;
            //accessors and mutators for start and end times
            public DateTime StartTime { get { return _startTime; } }
            public DateTime EndTime { get { return _endTime; } set { _endTime = value; } }
            
            //accessor for title of song
            public string getSong()
            {
                return _song;
            }

            //accessor for amount of time spent during session
            public TimeSpan getTime()
            {
                if (_endTime == null)
                {
                    _endTime = DateTime.Now;
                }
                return _endTime.Subtract(_startTime);
            }

            //accessor for difficulty--currently we only have 1 difficulty level
            public int getDifficulty()
            {
                return 1;
            }

            //mutator for percent correct
            public void setPercentCorrect(double percent)
            {
                _percentCorrect = percent;
            }

            //accessor for percent correct
            public double getPercentCorrect()
            {
                return _percentCorrect;
            }

            public void storeToDatabase()
            {
                //Database.writeMusicSessionData(this);
            }
        }
}